/*
The MIT License (MIT)

Copyright (c) 2013 Mike Dapiran, Brian May, Richard Pospesel, and Bert Wierenga

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software 
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#pragma once

#include <stddef.h>

#include "../Utils/Exception.h"
#include "../Math/Random.h"

namespace Hogshead
{
	namespace Common
	{
		template<typename T> class IEnumerator;
		template <typename T> class IList
		{
		public:
			/**
			* Adds an element to this List.  This method is pure virtual and must implemented by the
			* child class.
			* @param in_t The element to add.
			*/
			virtual void add(T in_t) = 0;
			/**
			* Inserts an element into the List.  All elements from the index to the right get shifted 
			* right by one.  This method is pure virtual and must be implemented by the child class.
			* @param in_index The index to put the element.
			* @param in_t The element to insert.
			*/
			virtual void insert(int in_index, T in_t) = 0;
			/**
			* Empties the list.  This method is pure virtual and must be implemented by the child class.
			*/
			virtual void clear() = 0;
			/**
			* Gets a copy of the element at the specified index.  This method is pure virtual and
			* musts be implemented by the child class.
			* @param in_index The index of the element to get a copy of.
			* @return A copy of the element at the specified index.
			*/
			virtual const T& get(int in_index) const = 0;
			/**
			* Gets a reference to the element at the specified index.  This method is pure
			* virtual and must be implemented by the child class.
			* @param in_index The index of the element to get a reference to.
			* @param A reference to the specified element.
			*/
			virtual T& get(int in_index) = 0;
			/**
			* Get a copy of the first element in the List.
			* @return A copy of the first element.
			*/
			virtual T first() const
			{
				return get(0);
			}
			/**
			* Get a reference to the first element in the List.
			* @return A reference to the first element.
			*/
			virtual T& first()
			{
				return get(0);
			}
			/**
			* Get a copy of the last element in the List.
			* @return A copy of the last element.
			*/
			virtual const T& last() const
			{
				return get(size() - 1);
			}
			/**
			* Get a reference to the last element in the List.
			* @return A reference to the last element.
			*/
			virtual T& last()
			{
				return get(size() - 1);
			}
			/**
			* Determine if the list has elements in it.  Returns true if
			* the list is empty (ie, size() == 0) and false otherwise.
			* @return Is the list empty.
			*/
			virtual bool isEmpty() const
			{
				return (size() == 0);
			}
			/**
			* Remove and return the element at the specified index.  This method
			* is pure virtual and must be implemented by child classes.
			* @param in_index Location of element to remove.
			* @return A copy of the element found at in_index.
			*/
			virtual T removeAt(int in_index) = 0;

			/** 
			* Remove and return a copy of the last element in the list.
			* @return A copy of the last element in the list.
			*/
			virtual T removeLast()
			{
				return removeAt(size() - 1);
			}

			/**
			* Removes the first instance of the passed in value, by swapping it to the end of the list, and then decreasing the 
			* length of the list by one by calling removeLast().
			* @param in_t The value to remove
			*/
			virtual bool swapRemove(T in_t)
			{
				for(int k = 0; k < size(); k++)
				{
					if((*this)[k] == in_t)
					{
						swap(k, size() - 1);
						removeAt(size() - 1);
						return true;
					}
				}
				return false;
			}

			virtual bool swapRemoveAt(int index)
			{
				if(index < 0)
					return false;

				if(index == size() - 1)
					removeLast();
				else
					(*this)[index] = removeLast();
				return true;
			}

			/**
			* Sets the element at the specified index to the passed in value.  This
			* method is pure virtual and must be implemented by child classes.
			* @param in_index Location to assign to.
			* @param in_t The element to copy into the List.
			*/

			virtual void set(int in_index, T in_t) = 0;
			/**
			* This method shuffles the elements in the List.
			*/
			virtual void shuffle()
			{
				Random rand;
				for(int k = size() - 1; k > 1; k--)
				{
					int swap_index = rand.nextInt(0, k-1);
					T temp = get(swap_index);
					
					set(swap_index, get(k));
					set(k, temp);
				}
			}
			/**
			* Returns the number of elements in the List.
			* @return The number of elements.
			*/
			virtual int size() const = 0;
			/**
			* Sorts the List using quicksort and the subscript operators.  Method
			* is virtual so child classes can implement more efficient methods.
			*/
			virtual void sort(int (*in_sort_function) (T, T))
			{

			}
			/**
			* Swaps the passed indeces using the get and set methods.
			* @param in_left The index of the first element.
			* @param in_right The index of the second element.
			*/
			virtual void swap(int in_left, int in_right)
			{
				if(in_left == in_right) return;

				T temp = get(in_left);
				set(in_left, get(in_right));
				set(in_right, temp);
			}
			const T& operator[](int in_index) const
			{
				return get(in_index);
			}
			T& operator[](int in_index)
			{
				if(in_index < 0)
				{
					throw Exception("Cannot access negative index");
				}
				return get(in_index);
			}
			/**
			* Returns a custom enumerator for this list class which lets you easily iterate over the lists elements.
			*/
			virtual IEnumerator<T>* getEnumerator() const
			{
				throw Exception("getEnumerator not implementend for this IList type");
			}
		};

	}
}